#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>

#include <unistd.h>
#include <fcntl.h>
#include <netdb.h>
#include <ifaddrs.h>
#include <errno.h>
#include <signal.h>
#include <time.h>
#include <libgen.h>
#include <dirent.h>
#include <pthread.h>
#include <semaphore.h>
#include <assert.h>
#include <ctype.h>

#include <arpa/inet.h>

#include <sys/types.h>
#include <sys/syscall.h>
#include <sys/ioctl.h>
#include <sys/prctl.h>
#include <sys/select.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/uio.h>
#include <sys/time.h>
#include <sys/wait.h>
#include <sys/time.h>
#include <sys/vfs.h>
#include <sys/mount.h>

#include <linux/unistd.h>
#include <linux/if.h>
#include <linux/wireless.h>

#include <net/route.h>

#include <netinet/in.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#include <netinet/tcp.h>
#include "xdata_test.h"
#include "getopt.h"
#include "bb_api.h"
#include "bb_dev.h"
#include "prj_rpc.h"
#include "pf_parser.h"
#include "util_api.h"

#define DBG_AR8030_GAP 3000
#define TX_PKT_TO  2000 /*uints:ms*/
#define TEST_TX_LEN 1024
#define HEAD_LEN 5

#define MCS_CFG_DEFUALT  -2
#define MCS_CFG_AUTO     -1

#define PWR_CFG_DEFUALT  -2
#define PWR_CFG_AUTO     -1

#define BB_DELAY_HIST_NUM 64
#define BB_INTVL_HIST_NUM 32
#define BB_FRM_SIZE_HIST_NUM 50

#define MAX_RINGBUF_LEN  (1024*1024*2)

/* Stats definitions */
uint64_t total_tx_dat_len = 0;
uint32_t total_tx_pkt_num = 0;
uint32_t max_tx_data_len = 0;
uint32_t min_tx_data_len = 0xffffffff;
uint64_t total_tx_dat_len_payload = 0;
uint32_t total_tx_pkt_num_payload = 0;
uint64_t total_delay_time = 0;

uint64_t total_rx_dat_len = 0;
uint32_t total_rx_pkt_num = 0;

uint64_t total_tx_dat_len_g = 0;
uint32_t max_tx_data_len_g = 0;
uint32_t min_tx_data_len_g = 0xffffffff;
uint32_t total_tx_pkt_num_g = 0;
uint64_t total_tx_dat_len_payload_g = 0;
uint32_t total_tx_pkt_num_payload_g = 0;
uint64_t total_delay_time_g = 0;

uint64_t total_rx_dat_len_g = 0;
uint32_t total_rx_pkt_num_g = 0;

extern uint64_t total_rx_dat_len_payload_g;
extern uint32_t total_rx_pkt_num_payload_g;
extern uint64_t total_rx_dat_len_payload;
extern uint32_t total_rx_pkt_num_payload;

static Task_Info_t  tx_task_info;
static Task_Info_t  rx_task_info;

static PF_AR8030_TX_THREAD_PARAM_t g_stAr8030TxThread;
PF_AR8030_RX_THREAD_PARAM_t g_stAr8030RxThread;

/* Running param definitions */
static bool is_reverse = false;
static bool is_ap = false;
static bool is_tx = true;

static bb_dev_handle_t *bb_handle;
static int slot = -1;
static int p_exit = 0;
static int signal_exit = 0;
static int bb_skt_fd;
static uint8_t bb_msg_id = 0;
static test_tx_len = TEST_TX_LEN;
static int trans_mode = 0;//0:socket 1:ioctl
static bool with_head = true;
static int socket_port = 0;
static bool wait_ack = false;
static int p_ack_done = false;

static uint32_t tx_delay = 1000*1000;

static unsigned char HEADATA[5]={0x00,0x00,0x00,0x01,0x80};

void xdata_demo_Usage(char * name)
{
    printf("Usage: %s [options]\n", name);
    printf("Options:\n");
    printf("\t-h       Print help info \n");
    printf("\t-d       DEV mode\n");
    printf("\t-p       AP mode\n");
    printf("\t-s       Slot id\n");
    printf("\t-b       Bandwidth, refers to bb_bandwidth_e, -1 for auto\n");
    printf("\t-y       Whether config frame change\n");
    printf("\t-m       MCS, refers to bb_phy_mcs_e\n");
    printf("\t-l       Tx frame payload length\n");
    printf("\t-o       Buffer size of the socket\n");
    printf("\t-c       Test time(unit:s) for tx\n");
    printf("\t-R       Test tp from dev to ap\n");
    printf("\t-P       Set power(-1 for auto)\n");
    printf("\t-M       Msgid of the pkt\n");
    printf("\t-D       Tx delay(unit:us)\n");
    printf("\t-T       Transmit mode(0:socket 1:ioctl)\n");
    printf("\t-H       Whether transfer with hdr\n");
    printf("\t-O       Port of the socket\n");
    printf("\t-a       Wait for ack from receiver\n");
}

void rx_data_cb(void* arg, void* user)
{
    bb_event_prj_dispatch2_t *event = (bb_event_prj_dispatch2_t *)arg;
    uint8_t* slot_id = (uint8_t*)user;
    msg_hdr_t Rxntdata;
    PF_AR8030_RX_Stats(slot_id, event->data, sizeof(bb_event_prj_dispatch2_t));

    Rxntdata.type=EVENT_IDLE;
    Rxntdata.is_stream=1;
    Rxntdata.data_len=sizeof(bb_event_prj_dispatch2_t);
    Rxntdata.dev=0;
    Rxntdata.data = event->data;
    PF_Parser_On_Event(&Rxntdata);
}

static void signal_hander(uint32_t signal)
{
    if(signal_exit == 0)
    {
        printf("__Application will exit by signal:%d, pid:%d\n", signal, getpid());
        if (signal == SIGSEGV)
        {
            SIG_DFL(signal);
        }

        signal_exit = 1;
    }
}

uint64_t MID_UTIL_Get_Time_ms(void)
{
    struct timeval tv;
	uint64_t ts_ms;
    gettimeofday(&tv, NULL);
	ts_ms = (uint64_t)tv.tv_sec*1000 + tv.tv_usec/1000;
    return ts_ms;
}

void PF_Add_Xdata_Head(PF_BB_XDATA_PKT_t *sync_data, uint16_t data_len, unsigned char msgid, \
								unsigned char user, unsigned char chl, unsigned char num_sum, \
                                unsigned char num_cur, uint16_t checksum)
{
	sync_data->sync_head[0]=0xFF;
	sync_data->sync_head[1]=0xA5;
	sync_data->sync_head[2]=0xAA;
	sync_data->sync_head[3]=0x5A;
	sync_data->sync_head[4]=0xFF;
	//data len
	sync_data->sync_head[5]=((data_len)>>0) & 0xFF;
	sync_data->sync_head[6]=((data_len)>>8) & 0xFF;
	//msgid
	sync_data->sync_head[7]= msgid;
	//user
	sync_data->sync_head[9]=user;
	//chl
	sync_data->sync_head[10]=chl;
    sync_data->sync_head[11]=num_sum;
    sync_data->sync_head[12]=num_cur;

    sync_data->sync_head[13]=((checksum)>>0) & 0xFF;
    sync_data->sync_head[14]=((checksum)>>8) & 0xFF;
}

uint32_t pkt_check_sum_cal(uint8_t* data, uint16_t data_size)
{
	uint32_t cs = 0;
	int i;
	for(i = 0; i < data_size; i++){
		cs += data[i];
	}
	return cs;
}

int xdata_ack_tx()
{
    int ret = 0;
	int wr_len;
    PF_BB_XDATA_PKT_t bb_pkt;
	int32_t synclen = SYNC_HEADLEN;
	unsigned char slot_id = BB_SLOT_0;

	int sendlen;
	void *sedndata;

    uint32_t checksum = 0;
	uint8_t *buffer;
	uint8_t *sendbuffer;

    bb_set_prj_dispatch2_in_t set_prj;
	prj_rpc_hdr_t *hdr;

	buffer=(uint8_t *)malloc(test_tx_len);
	sendbuffer=(uint8_t *)malloc(test_tx_len + SYNC_HEADLEN);
    //build bb pkt
    memset(buffer, 0xCD, test_tx_len);
    wr_len = test_tx_len;

    memcpy(bb_pkt.data, buffer, wr_len);

    checksum = pkt_check_sum_cal(bb_pkt.data, wr_len);

    /*now only length and msgid is valid*/
    PF_Add_Xdata_Head(&bb_pkt, wr_len, bb_msg_id, slot_id, 0, 1, 1, (uint16_t)checksum);

    sendlen = synclen + wr_len;
    sedndata = (void *)&bb_pkt;

    /*stats*/
    if(true)
    {
        if(sendlen > max_tx_data_len)
        {
            max_tx_data_len = sendlen;
            max_tx_data_len_g = sendlen;
        }
        if(sendlen < min_tx_data_len)
        {
            min_tx_data_len = sendlen;
            min_tx_data_len_g = sendlen;
        }
        total_tx_dat_len += sendlen;
        total_tx_dat_len_g += sendlen;

        total_tx_pkt_num++;
        total_tx_pkt_num_g++;

        total_tx_dat_len_payload_g += wr_len;
        total_tx_dat_len_payload += wr_len;

        total_tx_pkt_num_payload_g++;
        total_tx_pkt_num_payload++;
    }

    hdr=(prj_rpc_hdr_t *)set_prj.data;
    hdr->cmdid = PRJ_CMD_SET_USER_DATA;
    memcpy(hdr->data, &bb_pkt, sizeof(set_prj.data) - ((uint32_t)hdr->data - (uint32_t)hdr));
    ret = bb_ioctl(bb_handle, BB_SET_PRJ_DISPATCH2, &set_prj, NULL);

    return ret;
}

int xdata_rx_callback(uint8_t* buf, uint32_t len, uint16_t msg_id)
{
    int ret = 0;

    if (wait_ack) {
        if (is_tx) {
            p_ack_done = true;
        }
        else {
            ret = xdata_ack_tx();
        }
    }

    return ret;
}

void * PF_AR8030_Task_TX(void * args)
{
    char achName[64] = {0};
    int32_t s32Ret = 0;

	uint8_t *buffer;
	uint8_t *sendbuffer;

	int wr_len;
    PF_BB_XDATA_PKT_t bb_pkt;
	int32_t synclen = SYNC_HEADLEN;

    bb_set_prj_dispatch2_in_t set_prj;
	prj_rpc_hdr_t *hdr;

	int sendlen;
	void *sedndata;

	uint64_t pretime=0,nowtime=0;
	int sentcnt =0;
	int have_sent=0;
	int init_dat_len=0;
	unsigned char soc_id=0;
	uint64_t tx_ts, ack_ts;
    uint32_t checksum = 0;
	unsigned char slot_id = BB_SLOT_0;

    g_stAr8030TxThread.status = TASK_STATUS_RUNNING;

	soc_id = tx_task_info.soc_id;
	slot_id = tx_task_info.slot_id;
	//set thread name
	sprintf(achName, "BBTx_Task");
    prctl(PR_SET_NAME, achName, 0, 0, 0);

	buffer=(uint8_t *)malloc(FRAME_MAX_LEN);
    memset(buffer, 0xCD, FRAME_MAX_LEN);

	sendbuffer=(uint8_t *)malloc(FRAME_MAX_LEN + SYNC_HEADLEN);

	PRINT_TRACE("start %s\n",__func__);
	while(1)
	{
		if(g_stAr8030TxThread.s32Ar8030TxThreadExit )
		{
            if (g_stAr8030TxThread.status == TASK_STATUS_SHUTTING_2) {
                PRINT_ERR("exit %s",__func__);
                break;
            }
            g_stAr8030TxThread.status = TASK_STATUS_SHUTTING;
		}
		if(g_stAr8030TxThread.s32sAr8030TxThreadSuspend)
		{
			PRINT_ERR("suspend %s",__func__);
			usleep(500*1000);
			continue;
		}

        p_ack_done = false;

		//build bb pkt
        memset(buffer, 0xCD, FRAME_MAX_LEN);
		wr_len = test_tx_len;

        memcpy(bb_pkt.data, buffer, wr_len);

        if (with_head) {
            checksum = pkt_check_sum_cal(bb_pkt.data, wr_len);

            /*now only length and msgid is valid*/
            PF_Add_Xdata_Head(&bb_pkt, wr_len, bb_msg_id, slot_id, 0, 1, 1, (uint16_t)checksum);

            sendlen = synclen + wr_len;
            sedndata = (void *)&bb_pkt;
        }
        else {
            memcpy(bb_pkt.data, buffer, wr_len);

            sendlen = wr_len;
            sedndata = (void *)&bb_pkt.data;
        }

        /*stats*/
		if(true)
		{
			if(sendlen > max_tx_data_len)
			{
				max_tx_data_len = sendlen;
                max_tx_data_len_g = sendlen;
			}
			if(sendlen < min_tx_data_len)
			{
				min_tx_data_len = sendlen;
                min_tx_data_len_g = sendlen;
			}
			total_tx_dat_len += sendlen;
            total_tx_dat_len_g += sendlen;

			total_tx_pkt_num++;
            total_tx_pkt_num_g++;

            total_tx_dat_len_payload_g += wr_len;
            total_tx_dat_len_payload += wr_len;

            total_tx_pkt_num_payload_g++;
            total_tx_pkt_num_payload++;
		}

		sentcnt =0;
		have_sent=0;
		tx_ts = MID_UTIL_Get_Time_ms();
		init_dat_len = sendlen;

        if (trans_mode == TRANS_MODE_SOCKET) {
            while((sendlen>0) &&(MID_UTIL_Get_Time_ms()-tx_ts < TX_PKT_TO))
            {
                // PF_Parser_Buf_Print("send data", &sedndata[have_sent], sendlen);//zy test
                sentcnt = bb_socket_write(bb_skt_fd, &sedndata[have_sent], sendlen, WRITE_TIMEOUT);
                if(sentcnt >0){
                    sendlen = sendlen-sentcnt;
                    have_sent +=sentcnt;
                }
                else{
                    PRINT_ERR("bb_socket_write%d : write  fail,ret=0x%x,have_sent=%d,sendlen=%d,init_dat_len=%d,tx_ts=%llu,ts=%llu",
                        bb_skt_fd,
                        sentcnt,have_sent,sendlen,init_dat_len,tx_ts,MID_UTIL_Get_Time_ms());
                    usleep(100);
                }
            }

            if(sendlen > 0)
            {
                PRINT_ERR("bb_socket_write timeout,remain=%d",sendlen);
            }
        }
        else {
            hdr=(prj_rpc_hdr_t *)set_prj.data;
            hdr->cmdid = PRJ_CMD_SET_USER_DATA;
            memcpy(hdr->data, &bb_pkt, sizeof(set_prj.data) - ((uint32_t)hdr->data - (uint32_t)hdr));
            bb_ioctl(bb_handle, BB_SET_PRJ_DISPATCH2, &set_prj, NULL);
        }

        if (g_stAr8030TxThread.status == TASK_STATUS_SHUTTING) {
            g_stAr8030TxThread.status = TASK_STATUS_SHUTTING_2;
        }

        usleep(tx_delay);

        if (wait_ack) {
            while(!p_ack_done) {
                nowtime = MID_UTIL_Get_Time_ms();
                if (nowtime-pretime > 100) {
                    break;
                }
                usleep(10);
            }
        }

        ack_ts = MID_UTIL_Get_Time_ms();

        total_delay_time += (ack_ts - tx_ts);
        total_delay_time_g += (ack_ts - tx_ts);

        nowtime = MID_UTIL_Get_Time_ms();
        if(nowtime-pretime > DBG_AR8030_GAP)
        {
            PRINT_INFO("tx data rate = %llu kbps, %llu bytes, %lu pkts\n",
                total_tx_dat_len*8/(nowtime-pretime),
                total_tx_dat_len,
                total_tx_pkt_num);

            PRINT_INFO("tx payload data rate = %llu kbps, %llu bytes, %lu pkts\n",
                total_tx_dat_len_payload*8/(nowtime-pretime),
                total_tx_dat_len_payload,
                total_tx_dat_len_payload);

            if (wait_ack) {
                PRINT_INFO("tx delay = %llu ms, %llu ms/pkt\n",
                    total_delay_time,
                    total_delay_time / total_tx_pkt_num);
            }

            PRINT_INFO("total tx data %llu bytes, total %lu pkts\n",
                total_tx_dat_len_g,
                total_tx_pkt_num_g);

            PRINT_INFO("total payload tx data %llu bytes, total %lu pkts\n",
                total_tx_dat_len_payload_g,
                total_tx_pkt_num_payload_g);

            if (wait_ack) {
                PRINT_INFO("total tx delay = %llu ms, %llu ms/pkt\n",
                    total_delay_time_g,
                    total_delay_time_g / total_tx_pkt_num_g);
                PRINT_INFO("rx data rate =%d kbps,total_rx_pkt_num=%d\n",
                    total_rx_dat_len*8/(nowtime-pretime),
                    total_rx_pkt_num);
                PRINT_INFO("rx payload data rate =%d kbps,total payload_rx_pkt_num=%d\n",
                    total_rx_dat_len_payload*8/(nowtime-pretime),
                    total_rx_pkt_num_payload);
                PRINT_INFO("rx all data len =%llu bytes,total _rx_pkt_num=%llu\n",
                    total_rx_dat_len_g,
                    total_rx_pkt_num_g);
                PRINT_INFO("rx all payload data len =%llu bytes,total payload_rx_pkt_num=%llu\n",
                    total_rx_dat_len_payload_g,
                    total_rx_pkt_num_payload_g);
            }

            printf("#######################################################\n\n");

            pretime = nowtime;

            /* reset the one loop stats */
            total_tx_dat_len = 0;
            max_tx_data_len = 0;
            min_tx_data_len = 0xfffffff;
            total_tx_pkt_num = 0;
            total_tx_dat_len_payload = 0;
            total_tx_pkt_num_payload = 0;
            total_delay_time = 0;
        }
	}

	PRINT_ERR("done");
	free(buffer);
	free(sendbuffer);

    if (trans_mode == TRANS_MODE_SOCKET) {
        bb_socket_close(bb_skt_fd);
    }

    g_stAr8030TxThread.status = TASK_STATUS_SHUTDOWN;

	return NULL;
}

void PF_AR8030_RX_Stats(uint8_t slot_id, uint8_t *data, unsigned int  data_len)
{
    total_rx_dat_len += data_len;
    total_rx_pkt_num++;

    total_rx_dat_len_g += data_len;
    total_rx_pkt_num_g++;
}

void init_event_data(void * arg,unsigned int len){
    msg_hdr_t * data = (msg_hdr_t*)arg;
    memcpy(data->sync_head,HEADATA,HEAD_LEN);
    data->channel=0;
    data->data_len=len;
    data->skip_next_frame_len=0;
    data->sync_id=0;
    data->is_stream=0;
	data->dev=0;
    data->type=EVENT_IDLE;
    data->user=0;
    data->data=(unsigned char*)malloc(len);
}

void * PF_AR8030_Task_RX(void * args)
{
	char achName[64] = {0};
	int32_t s32Ret = 0;
	int ret;
	uint8_t *buffer=(uint8_t *)malloc(FRAME_MAX_LEN);
	uint8_t soc_id = rx_task_info.soc_id;
	uint8_t slot_id = rx_task_info.slot_id;

	uint32_t len = FRAME_MAX_LEN;
	msg_hdr_t Rxntdata;
    uint64_t pretime = 0, nowtime = 0;

    g_stAr8030TxThread.status = TASK_STATUS_RUNNING;

	sprintf(achName, "BBRX_Task");
    prctl(PR_SET_NAME, achName, 0, 0, 0);

	init_event_data(&Rxntdata, FRAME_MAX_LEN);

    PRINT_INFO("PF_AR8030_Task_RX start!!");

	PRINT_TRACE("start,soc_id = %d slot_id=%d", soc_id, slot_id);
	while(1)
	{
        if(g_stAr8030RxThread.s32Ar8030RxThreadExit )
		{
			PRINT_ERR("exit");
            break;
        }

		if(g_stAr8030RxThread.s32sAr8030RxThreadSuspend){
			usleep(500*1000);
			continue;
		}

        if (trans_mode == TRANS_MODE_SOCKET) {
            ret = bb_socket_read(bb_skt_fd, buffer, len, READ_TIMEOUT);
            if(ret > 0) {
                // printf("bb_socket_read get %d\n",ret);//zy test
                PF_AR8030_RX_Stats(slot_id, buffer, ret);

                Rxntdata.type=EVENT_IDLE;
                Rxntdata.is_stream=1;
                Rxntdata.data_len=ret;
                Rxntdata.dev=0;
                Rxntdata.data = buffer;
                if (with_head) {
                    Rxntdata.with_hdr = true;
                }
                else {
                    Rxntdata.with_hdr = false;
                }
                PF_Parser_On_Event(&Rxntdata);
            }
        }

        nowtime = MID_UTIL_Get_Time_ms();
        if(nowtime - pretime > DBG_AR8030_GAP)
        {
            PRINT_INFO("rx data rate =%d kbps,total_rx_pkt_num=%d\n",
                total_rx_dat_len*8/(nowtime-pretime),
                total_rx_pkt_num);
            PRINT_INFO("rx payload data rate =%d kbps,total payload_rx_pkt_num=%d\n",
                total_rx_dat_len_payload*8/(nowtime-pretime),
                total_rx_pkt_num_payload);
            PRINT_INFO("rx all data len =%llu bytes,total _rx_pkt_num=%llu\n",
                total_rx_dat_len_g,
                total_rx_pkt_num_g);
            PRINT_INFO("rx all payload data len =%llu bytes,total payload_rx_pkt_num=%llu\n",
                total_rx_dat_len_payload_g,
                total_rx_pkt_num_payload_g);
            printf("#############################################################\n\n");

            pretime = nowtime;
            total_rx_dat_len=0;
            total_rx_pkt_num = 0;
            total_rx_dat_len_payload = 0;
            total_rx_pkt_num_payload = 0;
        }
	}

	// uninit_event_data(&Rxntdata);
	free(buffer);
    if (trans_mode == TRANS_MODE_SOCKET) {
        bb_socket_close(bb_handle);
    }

    g_stAr8030RxThread.status = TASK_STATUS_SHUTDOWN;

	return NULL;
}

void PF_link_status_print(int slot)
{
    bb_get_status_in_t bb_st_in = {0};
    bb_get_status_out_t bb_st_out;

    bb_st_in.user_bmp = (1<<slot);
    bb_ioctl(bb_handle, BB_GET_STATUS, &bb_st_in, &bb_st_out);
    printf("[%d],role=%d,mode=%d,sync_mode=%d,sync_master=%d,cfg_sbmp=0x%0x,rt_sbmp=0x%0x\n",
        0,bb_st_out.role,bb_st_out.mode,
        bb_st_out.sync_mode,bb_st_out.sync_master,bb_st_out.cfg_sbmp,bb_st_out.rt_sbmp);

    printf("slot%d\n",slot);

    printf("dev_%d",0);
    printf("link_status:state=%d,rx_mcs=%d\n",
        bb_st_out.link_status[slot].state,bb_st_out.link_status[slot].rx_mcs);
    printf("user_status:tx_mcs=%d,tx_rf_mode=%d,tx_tintlv_enable=%d,tx_tintlv_len=%d,"
        "tx_tintlv_num=%d,tx_bw=%d,tx_freq=%lu kHz\n",
        bb_st_out.user_status[slot].tx_status.mcs,bb_st_out.user_status[slot].tx_status.rf_mode,
        bb_st_out.user_status[slot].tx_status.tintlv_enable,bb_st_out.user_status[slot].tx_status.tintlv_len,
        bb_st_out.user_status[slot].tx_status.tintlv_num,bb_st_out.user_status[slot].tx_status.bandwidth,
        bb_st_out.user_status[slot].tx_status.freq_khz);

    printf("user_status:rx_mcs=%d,rx_rf_mode=%d,rx_tintlv_enable=%d,rx_tintlv_len=%d,"
        "rx_tintlv_num=%d,rx_bw=%d,rx_freq=%lu kHz\n",
        bb_st_out.user_status[slot].rx_status.mcs,bb_st_out.user_status[slot].rx_status.rf_mode,
        bb_st_out.user_status[slot].rx_status.tintlv_enable,bb_st_out.user_status[slot].rx_status.tintlv_len,
        bb_st_out.user_status[slot].rx_status.tintlv_num,bb_st_out.user_status[slot].rx_status.bandwidth,
        bb_st_out.user_status[slot].rx_status.freq_khz);
}

int main(int argc, char ** argv)
{
	int s32Ret;
    bool is_frame_change = false;
    int dev_num;
    int mcs = MCS_CFG_DEFUALT;
    int wait_times = 0;
    int bandwidth = 0;
    int buffer_size = 40*1024;
    int test_time = -1;
    int power = PWR_CFG_DEFUALT;
    bb_host_t* phost=NULL;
    bb_dev_t** devs=NULL;
    uint32_t flag =BB_SOCK_FLAG_RX|BB_SOCK_FLAG_TX;
    bb_sock_opt_t sock_data_buf_size;
	bb_set_prj_dispatch_in_t set_prj;
	prj_rpc_hdr_t *hdr;
	prj_cmd_event_pair_t *event_pair;
    bb_event_callback pcb = NULL;
    bb_set_event_callback_t cb;
    bb_set_frame_change_t bb_set_frame_change;
    bb_set_mcs_mode_t bb_set_mcs_mode;
    bb_set_mcs_t bb_set_mcs;
    bb_set_bandwidth_t bb_set_bandwidth;
    bb_set_pwr_auto_in_t bb_set_pwr_auto_in;
    bb_set_pwr_mode_in_t bb_set_pwr_mode_in;
    bb_set_pwr_in_t bb_set_pwr_in;
    bb_get_sys_info_out_t bb_get_sys_info_out;
    int ret;
    uint64_t now_ms, start_ms;

    if(argc == 1){
	    xdata_demo_Usage(argv[0]);
		return -1;
	}

    while((s32Ret = getopt(argc, argv, "hdps:ym:b:l:o:c:RP:D:M:T:H:O:a")) != -1)
    {
        switch(s32Ret)
		{
            case 'h':
                xdata_demo_Usage(argv[0]);
                return 0;
            case 'd':
                printf("dev mode\n");
                is_ap = false;
                break;
            case 'p':
                printf("ap mode\n");
                is_ap = true;
                break;
            case 's':
                slot = atoi(optarg);
                printf("slot %d\n", slot);
                break;
            case 'y':
                is_frame_change = true;
                printf("frame change\n");
                break;
            case 'm':
                mcs = atoi(optarg);
                printf("mcs = %d\n",mcs);
                break;
            case 'b':
                bandwidth = atoi(optarg);
                printf("bandwidth = %d\n",bandwidth);
                break;
            case 'l':
                test_tx_len = atoi(optarg);
                printf("test_tx_len = %d\n",test_tx_len);
                break;
            case 'o':
                buffer_size = atoi(optarg);
                printf("buffer_size = %d\n",buffer_size);
                break;
            case 'c':
                test_time = atoi(optarg);
                printf("test_time = %d\n",test_time);
                break;
            case 'R':
                is_reverse = true;
                printf("reverse\n");
                break;
            case 'P':
                power = atoi(optarg);
                printf("power = %d\n",power);
                break;
            case 'D':
                tx_delay = atoi(optarg);
                printf("tx_delay = %d\n",tx_delay);
                break;
            case 'M':
                bb_msg_id = atoi(optarg);
                printf("bb_msg_id = %d\n",bb_msg_id);
                break;
            case 'T':
                trans_mode = atoi(optarg);
                printf("trans_mode = %d\n",trans_mode);
                break;
            case 'H':
                with_head = atoi(optarg);
                printf("with_head = %d\n",with_head);
                break;
            case 'O':
                socket_port = atoi(optarg);
                printf("socket_port = %d\n",socket_port);
                break;
            case 'a':
                wait_ack = true;
                printf("wait for ack after each tx\n");
                break;
            default:
                printf("unknow cmd param !\n");
    			xdata_demo_Usage(argv[0]);
                return -1;
        }
    }

    signal(SIGINT, signal_hander);

    if ((!is_reverse && is_ap) ||
        (is_reverse && !is_ap)) {
        is_tx = true;
    }
    else {
        is_tx = false;
    }

    printf("dir:%s\n",is_tx?"tx":"rx");

    printf("%s compiled at: %s %s\n", argv[0], __DATE__, __TIME__);

	if (is_ap && (slot == 255)) {
        PRINT_ERR("ap should assign the slot!\n");
        return -1;
    }

	s32Ret = bb_host_connect(&phost,"127.0.0.1",BB_PORT_DEFAULT);
	if(s32Ret)
	{
		PRINT_ERR("bb connect error,ret=%d",s32Ret);
		//bb_deinit();
		return -1;
	}

    PRINT_INFO("bb_host_connect ret %d", ret);

    dev_num = bb_dev_getlist(phost,&devs);

    PRINT_INFO("bb_dev_getlist dev_num %d",dev_num);

    bb_handle = bb_dev_open(devs[0]);

    if (!bb_handle) {
		printf("bb_dev_open error");
		//bb_deinit();
		return -1;
    }

    bb_dev_freelist(devs);

    PRINT_INFO("bb_dev_freelist");

    s32Ret = bb_ioctl(bb_handle, BB_GET_SYS_INFO, NULL, &bb_get_sys_info_out);
    if (s32Ret) {
        printf("BB_GET_SYS_INFO failed s32Ret = %d\n", s32Ret);
    }

    printf("compile_time : %s\n", bb_get_sys_info_out.compile_time);
    printf("soft_ver : %s\n", bb_get_sys_info_out.soft_ver);
    printf("hardware_ver : %s\n", bb_get_sys_info_out.hardware_ver);
    printf("firmware_ver : %s\n", bb_get_sys_info_out.firmware_ver);

    if (trans_mode == TRANS_MODE_SOCKET) {
        sock_data_buf_size.rx_buf_size=1*1024;
        sock_data_buf_size.tx_buf_size=1*1024;
        if(is_tx) {
            sock_data_buf_size.tx_buf_size=buffer_size;//40*1024;
        }
        else {
            sock_data_buf_size.rx_buf_size=buffer_size;//40*1024;
        }

        bb_skt_fd = bb_socket_open(bb_handle,
                                is_ap?slot:0,
                                socket_port,
                                flag,
                                &sock_data_buf_size);
        PRINT_INFO("bb_skt_fd %d", bb_skt_fd);
    }

    if (power == PWR_CFG_AUTO) {
        /* power auto */
        bb_set_pwr_auto_in.pwr_auto = 1;
        bb_ioctl(bb_handle, BB_SET_POWER_AUTO, &bb_set_pwr_auto_in, NULL);
    }
    else if (power >= 0) {
        /* power auto */
        bb_set_pwr_auto_in.pwr_auto = 0;
        bb_ioctl(bb_handle, BB_SET_POWER_AUTO, &bb_set_pwr_auto_in, NULL);

        /* power low */
        bb_set_pwr_in.usr = BB_USER_BR_CS;
        bb_set_pwr_in.pwr = power;
        bb_ioctl(bb_handle, BB_SET_POWER, &bb_set_pwr_auto_in, NULL);
    }


    if (is_ap && is_tx) {
        /* set frame change*/
        if (is_frame_change) {
            bb_set_frame_change.mode = 1;
        }
        else {
            bb_set_frame_change.mode = 0;
        }

        ret = bb_ioctl(bb_handle, BB_SET_FRAME_CHANGE, &bb_set_frame_change, NULL);
        PRINT_INFO("BB_SET_FRAME_CHANGE %d ret %d\n", bb_set_frame_change.mode, ret);
    }

    usleep(3000000);

    /* set bandwidth*/
    if (bandwidth) {
        bb_set_bandwidth.bandwidth = bandwidth;
        if (is_tx) {
            bb_set_bandwidth.dir = BB_DIR_TX;
        }
        else {
            bb_set_bandwidth.dir = BB_DIR_RX;
        }

        bb_set_bandwidth.slot = slot;
        ret = bb_ioctl(bb_handle, BB_SET_BANDWIDTH, &bb_set_bandwidth, NULL);
        printf("BB_SET_BANDWIDTH bandwidth %d ret %d\n", bandwidth, ret);
    }

    /*set mcs*/
    if (mcs == MCS_CFG_AUTO) {
        bb_set_mcs_mode.auto_mode = true;
        bb_set_mcs_mode.slot = slot;
        s32Ret = bb_ioctl(bb_handle, BB_SET_MCS_MODE, (void*)&bb_set_mcs_mode, NULL);
        if (s32Ret) {
            PRINT_ERR("set mcs mode err! ret = %d\n",s32Ret );
            return -1;
        }
    }
    else if (mcs >= 0){
        bb_set_mcs_mode.auto_mode = false;
        bb_set_mcs_mode.slot = slot;
        s32Ret = bb_ioctl(bb_handle, BB_SET_MCS_MODE, (void*)&bb_set_mcs_mode, NULL);
        if (s32Ret) {
            PRINT_ERR("set mcs mode err! ret = %d\n",s32Ret );
            return -1;
        }

        bb_set_mcs.mcs = mcs;
        bb_set_mcs.slot = 0;
        s32Ret = bb_ioctl(bb_handle, BB_SET_MCS, (void*)&bb_set_mcs, NULL);
        if (s32Ret) {
            PRINT_ERR("set mcs err! ret = %d\n",s32Ret );
            return -1;
        }
    }

    /* set bandwidth*/
    if (bandwidth) {
        bb_set_bandwidth.bandwidth = bandwidth;
        if (is_tx) {
            bb_set_bandwidth.dir = BB_DIR_TX;
        }
        else {
            bb_set_bandwidth.dir = BB_DIR_RX;
        }

        bb_set_bandwidth.slot = slot;
        ret = bb_ioctl(bb_handle, BB_SET_BANDWIDTH, &bb_set_bandwidth, NULL);
        printf("BB_SET_BANDWIDTH bandwidth %d ret %d\n", bandwidth, ret);
    }

    if (is_tx) {
        if (wait_ack) {
            PF_Parser_Init(MAX_RINGBUF_LEN);
            /* Register the rx data callback */
            pcb = (bb_event_callback)rx_data_cb;
            cb.callback = pcb;
            cb.event    = BB_EVENT_PRJ_DISPATCH2;
            cb.user     = (void*)&slot;
            s32Ret = bb_ioctl(bb_handle, BB_SET_EVENT_SUBSCRIBE, &cb, 0);
            if (s32Ret) {
                PRINT_ERR("set receive callback err! ret = %d\n",s32Ret );
                return -1;
            }
            printf("BB_EVENT_PRJ_DISPATCH2 register ret %d\n", s32Ret);
        }

        tx_task_info.soc_id = 0;
        tx_task_info.slot_id = slot;
        s32Ret = MID_UTIL_CreateDetachThread(PF_AR8030_Task_TX, NULL, NULL);
        if(s32Ret < 0)
        {
            PRINT_ERR("create ARCAST_AR8030_Task_TX fail!\n");
            return -1;
        }
    }
    else {
        PF_Parser_Init(MAX_RINGBUF_LEN);

        if (trans_mode == TRANS_MODE_IOCTL) {
            /* Register the rx data callback */
            pcb = (bb_event_callback)rx_data_cb;
            cb.callback = pcb;
            cb.event    = BB_EVENT_PRJ_DISPATCH2;
            cb.user     = (void*)&slot;
            s32Ret = bb_ioctl(bb_handle, BB_SET_EVENT_SUBSCRIBE, &cb, 0);
            if (s32Ret) {
                PRINT_ERR("set receive callback err! ret = %d\n",s32Ret );
                return -1;
            }
            printf("BB_EVENT_PRJ_DISPATCH2 register ret %d\n", s32Ret);
        }

        /* Create ringbuffer for rx */
        PRINT_INFO("Create ringbuffer for rx");

        rx_task_info.soc_id = 0;
        rx_task_info.slot_id = slot;
        s32Ret = MID_UTIL_CreateDetachThread(PF_AR8030_Task_RX, NULL, NULL);
        if(s32Ret < 0)
        {
            PRINT_ERR("create PF_AR8030_Task_RX fail!\n");
            return -1;
        }
    }

    start_ms = MID_UTIL_Get_Time_ms();

    while(1) {
        now_ms = MID_UTIL_Get_Time_ms();
        if (is_tx && ((g_stAr8030TxThread.status == TASK_STATUS_SHUTDOWN) || signal_exit)) {
                PRINT_INFO("####TX total pkt %lu bytes %lu bytes rate %lukbps time:%ds ########\n",
                        total_tx_pkt_num_g,
                        total_tx_dat_len_g,
                        total_tx_dat_len_g*8/(now_ms - start_ms),
                        ((now_ms - start_ms)/1000));
                PRINT_INFO("####TX payload pkt %lu bytes %lu bytes rate %lukbps time:%ds ########\n",
                        total_tx_pkt_num_payload_g,
                        total_tx_dat_len_payload_g,
                        total_tx_dat_len_payload_g*8/(now_ms - start_ms),
                        ((now_ms - start_ms)/1000));

                if (wait_ack) {
                    PRINT_INFO("####Rx delay = %llu ms, %llu ms/pkt\n",
                        total_delay_time_g,
                        total_delay_time_g / total_tx_pkt_num_g);

                    PRINT_INFO("####RX total pkt %lu bytes %lu bytes rate %lukbps time:%ds########\n",
                            total_rx_pkt_num_g,
                            total_rx_dat_len_g,
                            total_rx_dat_len_g*8/(now_ms - start_ms),
                            ((now_ms - start_ms)/1000));
                    PRINT_INFO("####RX payload pkt %lu bytes %lu bytes rate %lukbps time:%ds########\n",
                            total_rx_pkt_num_payload_g,
                            total_rx_dat_len_payload_g,
                            total_rx_dat_len_payload_g*8/(now_ms - start_ms),
                            ((now_ms - start_ms)/1000));
                }
                break;
        }
        else if (!is_tx && ((g_stAr8030RxThread.status == TASK_STATUS_SHUTDOWN) || signal_exit)) {
            PRINT_INFO("####RX total pkt %lu bytes %lu bytes rate %lukbps time:%ds########\n",
                    total_rx_pkt_num_g,
                    total_rx_dat_len_g,
                    total_rx_dat_len_g*8/(now_ms - start_ms),
                    ((now_ms - start_ms)/1000));
            PRINT_INFO("####RX payload pkt %lu bytes %lu bytes rate %lukbps time:%ds########\n",
                    total_rx_pkt_num_payload_g,
                    total_rx_dat_len_payload_g,
                    total_rx_dat_len_payload_g*8/(now_ms - start_ms),
                    ((now_ms - start_ms)/1000));
            break;
        }
        else if (is_tx && (g_stAr8030TxThread.status == TASK_STATUS_RUNNING)) {
            if ((test_time >= 0) &&((now_ms - start_ms)/1000) >= test_time) {
                g_stAr8030TxThread.s32Ar8030TxThreadExit = true;
            }
        }

        usleep(10);
    }

    if (!is_tx) {
        PF_Parser_Deinit();
    }

    printf("test end, exit!\n");

    exit(0);
}
